package pso;

import run.Utility;
import function.Function;
import function.Rastrigin;

public class Particle {
	
	Function f;
	private double velocity[];
	private double [] posicaoAtual;
	private double [] pBest;
	private double [] nBest;
	
	public double[] getnBest() {
		return nBest;
	}

	public void setnBest(double[] nBest) {
		this.nBest = nBest;
	}

	private double fitness;
	private int index;
	
	
	public Particle(Function f){
		this.f = f;
		initialize();
	}
	
	public Particle(Function f, int index){
		this.f = f;
		this.index = index;
		initialize();
	}
	
	private void initialize() {	
		//inicializa a dimensao do invididuo
		double value;
		this.posicaoAtual = new double[this.f.getNumDimensions()];
		this.pBest = new double[this.f.getNumDimensions()];
		this.velocity = new double[this.f.getNumDimensions()];
		
		//inicializar cada espaço do vetor com um número entre f.getMinBoudaries() e f.getMaxBoudaries()
		for (int i = 0; i < posicaoAtual.length; i++) {
			value = Utility.between(f.getMinBoudaries(), f.getMaxBoudaries());
			posicaoAtual[i] = value;
			pBest [i] = value; //quando eu crio uma particula minha melhor posicao � a inicial (aula 20/11/2012)
		}
		this.fitness = this.f.calculate(posicaoAtual);
				
	}
	
	
	public static void main(String[] args) {
		new Particle(new Rastrigin());
	}
	
	public int getIndex() {
		return index;
	}

	public void setIndex(int index) {
		this.index = index;
	}

	//global best PSO - calcula minha velocidade - equacao (16.2) de Engelbrecht
	public void calculateVelocity(double [] bestPositionSwarm, int iteration, int numberIterations){
			
		double w = calculateInertia(iteration, numberIterations);
		double c1 = calculateC1(iteration,numberIterations);
		double c2 = calculateC2(iteration,numberIterations);
		for (int i = 0; i < this.f.getNumDimensions(); i++) {			
			velocity[i] = w * velocity[i] + c1 * Utility.between(0.0, 1) * (pBest[i] - posicaoAtual[i]) +
										    c2 * Utility.between(0.0, 1) * (bestPositionSwarm[i] - posicaoAtual[i]);
		}
		/*
		for (int i = 0; i < this.f.getNumDimensions(); i++) {			
			velocity[i] = w * velocity[i] + PSO_Constants.c1Inicial * Utility.between(0.0, 1) * (pBest[i] - posicaoAtual[i]) +
										    PSO_Constants.c2Inicial * Utility.between(0.0, 1) * (bestPositionSwarm[i] - posicaoAtual[i]);
		}
		*/
	}
	
	private double calculateC2(int iteration, int numberIteration) {
		double inertia = 0;
		inertia = (PSO_Constants.c2Inicial - PSO_Constants.c2Final) * 
				((numberIteration-iteration)/((double)numberIteration)) + PSO_Constants.c2Final;
		
		
		return inertia;	}

	private double calculateC1(int iteration, int numberIteration) {
		double inertia = 0;
		inertia = (PSO_Constants.c1Inicial - PSO_Constants.c1Final) * 
				((numberIteration-iteration)/((double)numberIteration)) + PSO_Constants.c1Final;
		
		
		return inertia;
	}

	private double calculateInertia (int iteration, int numberIteration){
		double inertia = 0;
		inertia = (PSO_Constants.WInicial - PSO_Constants.WFinal) * 
				((numberIteration-iteration)/((double)numberIteration)) + PSO_Constants.WFinal;
		
		
		return inertia;
		
	}
	
	//atualiza minha melhor posi��o encontrada - equa��o (16.3) do Engelbrecht
	public void calculatePersonalBestPosition(){
			double atual = f.calculate(this.posicaoAtual);
			
			if( atual < f.calculate(pBest) ){
				pBest = posicaoAtual.clone();
				this.fitness = atual;
			}
	}
	
	
	//atualiza minha posicao: x i(t+1)= x i(t)+ v i(t + 1) - equa��o (16.1) do Engelbrecht
	public void calculatePosition(){
		for (int i = 0; i < posicaoAtual.length; i++) {
			
			if( (posicaoAtual[i] + velocity[i]) > f.getMaxBoudaries() ){
				velocity[i] = velocity[i] * (-1); //inverte o sentido da part�cula
				posicaoAtual[i] = f.getMaxBoudaries();
			}else if ((posicaoAtual[i] + velocity[i]) < f.getMinBoudaries() ) {
				velocity[i] = velocity[i] * (-1); //inverte o sentido da part�cula
				posicaoAtual[i] = f.getMinBoudaries();
			}else{
				posicaoAtual[i] = (posicaoAtual[i] + velocity[i]);
			}
		}
		
	}
		
	public double[] getVelocity() {
		return velocity;
	}
	
	public void setVelocity(double[] velocity) {
		this.velocity = velocity;
	}

	public double[] getPosicaoAtual() {
		return posicaoAtual;
	}

	public void setPosicaoAtual(double[] posicaoAtual) {
		this.posicaoAtual = posicaoAtual;
	}

	public double[] getPersonalBestPosition() {
		return pBest;
	}

	public void setPersonalBestPosition(double[] personalBestPosition) {
		this.pBest = personalBestPosition;
	}

	public double getFitness() {
		return fitness;
	}

	
	
	
	
}
